-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local class = require 'mc.class'
local upgrade_scenarios = require 'domain.bios_firmware.package.upgrade_scenarios'
local bios_enum = require 'domain.bios_firmware.defs'
local upgrade_config_builder = require 'domain.bios_firmware.package.upgrade_config_builder'
local log = require 'mc.logging'
local Package = class()

local AllOWED_BIOS_LOW_SECURITY_UPGRADE<const> = 1 --仅允许低安全包升级

function Package:ctor(package_config, package_type, period, version, uid_list)
    -- 升级包配置
    self.package_config = package_config
    -- 升级包类型：正常包、补丁包
    self.package_type = package_type
    self.period = period
    -- 包版本,例如：6.60.HP0001，其中6.60为基础版本，HP0001为补丁版本
    self.version = version
    self.uid_list = uid_list
end

function Package:dtor()
    self.package_config = nil
    self.package_type = nil
    self.period = nil
    self.version = nil
    self.uid_list = nil
end

local function get_basic_version(version)
    local basic_version = version
    local v1, v2 = string.match(version, '(%d+).(%d+)')
    if v1 and v2 then
        basic_version = v1 .. '.' .. v2
    end
    return basic_version
end

--包的类型 环境上允许升级的类型 是否允许升级
--安全          安全            是
--安全         低安全           否
--低安全        安全            否
--低安全       低安全           是
--不同PackageType属性的Bios包隔离升级
function Package:is_allowed_bios_upgrade(allowed_package_type)
    local cfg = self.package_config:get_cfg()
    -- 升级包的PackageType字段如果不存在，仅支持升级安全包
    if not cfg.package_type then
        log:notice("[bios]bios package is security type")
        return allowed_package_type ~= AllOWED_BIOS_LOW_SECURITY_UPGRADE
    end
    log:notice("[bios]bios package is low security type")
    return allowed_package_type == AllOWED_BIOS_LOW_SECURITY_UPGRADE
end

function Package:prepare(cfg)
    if self.package_type ~= bios_enum.PackageType.Patch then
        return
    end
    if get_basic_version(cfg.Version) ~= get_basic_version(self.version) then
        log:maintenance(log.MLOG_ERROR, log.FC__PUBLIC_OK,
            'verify bios patch package version failed, upgrade version is %s, local version is %s',
            self.version, cfg.Version)
        error(string.format('[bios]upgrade: package dismatch, basic version(%s), patch version(%s)',
            cfg.Version, self.version))
    end
end

function Package:_init_package_snapshot(snapshot)
    snapshot:set_effective(bios_enum.ActivateStatus.Begin)
    snapshot:set_period(self.period)
    snapshot:set_package_type(self.package_type)
    if self.package_type ~= bios_enum.PackageType.Krun then
        snapshot:set_version(self.version)
    end
end

local name_map = {
    [bios_enum.PackageType.Normal] = 'bios.bin',
    [bios_enum.PackageType.Patch] = 'bios.bin',
    [bios_enum.PackageType.Tee] = 'tmm.bin',
    [bios_enum.PackageType.Krun] = 'Krun.bin'
}

function Package:get_bin_name()
    if not name_map[self.package_type] then
        error(string.format('[bios]package: get bin name fail, package type(%s) invalid'), self.package_type)
    end
    return name_map[self.package_type]
end

function Package:process(upgrade_info, snapshot)
    self:_init_package_snapshot(snapshot)
    self:_start(upgrade_info, snapshot)
end

-- 1、拷贝包
-- 2、缓存包
-- 3、强制下电操作
-- 4、上电锁
-- 5、加载驱动
-- 6、解析包
-- 7、选择升级区域
-- 8、选择升级通道
-- 9、开始升级

--                      装备模式            正常模式
-- 拷贝包
-- 缓存包                     否                 是
-- 操作上下电          强制上下电              不涉及
-- 加载驱动
-- 解析包            解析装备区域      解析非装备区域
-- 升级

--                      冷升级                热升级（补丁包、开启热升级）
-- 拷贝包
-- 缓存包
-- 操作上下电
-- 加载驱动
-- 解析包
-- 选择通道             flash                   ipmb、flash
-- 升级

-- 拷贝包
-- 缓存包
-- 操作上下电
-- 加载驱动                                    降频
-- 解析包              固定格式              按header解析
-- 升级                冷升级                  升级
-- 升级模式（普通模式、强制模式）
function Package:_start(upgrade_info, snapshot)
    local scenarios
    -- 通过配置创建升级的执行链
    local ok, err = pcall(function()
        local package_info = {
            period = self.period,
            package_type = self.package_type
        }
        local cfg_builder = upgrade_config_builder.new(upgrade_info, package_info)
        local upgrade_cfg = cfg_builder:build()
        scenarios = upgrade_scenarios.new(upgrade_cfg)
    end)
    if not ok then
        error(string.format('[bios] build scenarios fail, %s', err))
    end
    -- executor执行链的上下文对象
    local ctx = {
        -- 全局上下文：不可更改
        Global = {
            UpgradeDBTable = self.package_type == bios_enum.PackageType.Krun and
                upgrade_info.db.KrunFirmware({Id = upgrade_info.krun_id}) or upgrade_info.db.BiosUpdateCfgTable({Id = 'BiosCfg'}),
            UpgradeMode = upgrade_info.upgrade_mode,
            Period = self.period,
            PackageType = self.package_type,
            PackageCfg = self.package_config,
            Snapshot = snapshot,
            SystemId = snapshot:get_system_id(),
            KrunId = upgrade_info.krun_id,
            hpm_delete_flag = upgrade_info.hpm_delete_flag
        },
        -- 链间上下文
        Interchain = {
            UpgradePath = upgrade_info.upgrade_path,
            BinName = self:get_bin_name(),
            -- 标识是否需要传包升级
            UpgradeFlag = true,
            SystemId = snapshot:get_system_id(),
            BlockChip = upgrade_info.block_chip,
            FlashChip = upgrade_info.flash_chip
        },
    }
    scenarios:execute(ctx)
end

function Package:obtain_process()
end

return Package